package com.zhaorui.enablescheduling;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zhaorui.entity.ActivityTitle;
import com.zhaorui.entity.ActivityTitleLevel;
import com.zhaorui.entity.TitlePrizeDTO;
import com.zhaorui.mapper.ActivityPrizeMapper;
import com.zhaorui.mapper.ActivityTitleLevelMapper;
import com.zhaorui.mapper.ActivityTitleMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import java.sql.Connection;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 静态执行
 */
@Configuration //1.主要用于标记配置类，兼备Component的效果。
@EnableScheduling // 2.开启定时任务
@Slf4j
public class SimpleScheduleConfig {
    @Autowired
    private ActivityTitleMapper activityTitleMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityTitleLevelMapper activityTitleLevelMapper;

    //3.添加定时任务
    @Scheduled(cron = "0 */1 * * * ?")
    private void configureTasks() {
        System.err.println ("执行静态定时任务: " + LocalDateTime.now ().toLocalTime ());

        /**
         * 1、预热5分钟以内的活动加载到redis中去
         */
        Date now = new Date ();
        List<ActivityTitle> activityTitleList = activityTitleMapper.queryTitleRedis (DateUtil.format (now, "yyyy-MM-dd HH:mm:ss"));
        if (activityTitleList.size () == 0) {
            log.info ("暂无活动");
            return;
        }
        //否则就是有活动显示：目前活动的个数
        log.info ("目前活动的个数{}:", activityTitleList.size ());
        //循环取出每一个活动缓存到redis中
        activityTitleList.forEach (activityTitle -> {
            log.info ("{}", activityTitle);
            activityTitle.setActivityState (1);//修改活动状态已经加载
            //title_info:创建文件夹，项目多的时候便于区分
            redisTemplate.opsForValue ().set ("title_info:" + activityTitle.getId (), activityTitle);

            /**
             /2、生成令牌桶缓存到redis中
             */
            //获取当前活动的配置奖品信息与数量
            List<TitlePrizeDTO> titlePrizeDTOS = activityPrizeMapper.queryByTitlePrizeQty (activityTitle.getId ());
            log.info ("{}", titlePrizeDTOS);
            //拿到活动的开始时间
            long startTime = activityTitle.getStarttime ().getTime ();//getTime ():拿到毫秒数
            //拿到结束时间
            long overTime = activityTitle.getOvertime ().getTime ();
            //拿到活动持续时间
            long duration = overTime - startTime;
            //活动失效时间:duration毫秒改成秒
            long expire = duration / 1000;

            //令牌桶集合
            List<Long> tokenList = new ArrayList<> ();
            //有多少个奖品就要生成多少个令牌
            int count = 0;//用于计算令牌数量
            for (TitlePrizeDTO titlePrizeDTO : titlePrizeDTOS) {
                for (int i = 0; i < titlePrizeDTO.getQty (); i++) {
                    //拿到持续期内的对应奖品数的令牌
                    long token = startTime + new Random ().nextInt ((int) duration);
                    //防止奖品过多，活动时间段，而导致令牌（时间戳重复）不够
                    token = token * 1000 + new Random ().nextInt (999);
                    /**
                     * 3、每个令牌桶与奖品绑定
                     * 一个时间戳对应一个奖品；放入redis中
                     */
                    redisTemplate.opsForValue ().set ("token_prize_" + activityTitle.getId () + ":" + token, titlePrizeDTO, expire / 60, TimeUnit.MINUTES);

                    //每生成一个令牌或者时间戳就放进令牌桶内
                    tokenList.add (token);
                    /**
                     * 4、缓存当前活动的中奖策略
                     */
                    QueryWrapper queryWrapper = new QueryWrapper ();
                    queryWrapper.eq ("titleid", activityTitle.getId ());
                    List<ActivityTitleLevel> ruleList = activityTitleLevelMapper.selectList (queryWrapper);
                    //循环取出每一个
                    for (ActivityTitleLevel activityTitleLevel : ruleList) {
                        //放入缓存中
                        //最大抽奖数:activityTitleLevel.getLevelid ()+"":必须加+“”。key:因为是字符串
                        redisTemplate.opsForHash ().put ("title_rule_enterCount:" + activityTitle.getId (), activityTitleLevel.getLevelid () + "", activityTitleLevel.getEnterQty ());
                        //最大中奖数
                        redisTemplate.opsForHash ().put ("title_rule_maxCount:" + activityTitle.getId (), activityTitleLevel.getLevelid () + "", activityTitleLevel.getMaxQty ());
                        //设置2个抽奖策略的失效时间
                        redisTemplate.expire ("title_rule_enterCount:" + activityTitle.getId (), expire, TimeUnit.SECONDS);
                        redisTemplate.expire ("title_rule_maxCount:" + activityTitle.getId (), expire, TimeUnit.SECONDS);
                    }


                    //记录次数
                    count++;
                }
            }
            log.info ("一共生成{}令牌对应的奖品数量是：{}", count, count);
            //生成的令牌或者时间戳是无序的，需从新排序
            Collections.sort (tokenList);
            //放入redis中，用List；并从集合的右边往左边推:活动对应奖品的配置信息
            redisTemplate.opsForList ().rightPush ("title_token:" + activityTitle.getId (), tokenList);
            //活动结束redis中的令牌桶应该全部取消缓存
            //redisTemplate.expire ("title_token:" + activityTitle.getId (), Duration.ofDays (expire));
            //设置令牌桶的失效
            redisTemplate.expire ("title_token:" + activityTitle.getId (), expire, TimeUnit.SECONDS);

            //修改数据库中的状态
            activityTitleMapper.updateById (activityTitle);
        });
    }


}